import * as d3 from 'd3'

interface Point {
  x: number
  y: number
}
interface TrajectoryData {
  data: Point[]
  id: number
}

class TrajectoryChart {
  svg: d3.Selection<SVGSVGElement, unknown, HTMLElement | null, unknown>
  trackData: TrajectoryData[] | null = null
  imageSize = { width: 720, height: 720 }  // 实际图片大小
  margin = { top: 40, left: 40 }
  size = { width: 640, height: 640 }  // svg大小，图表大小加上边距
  xScale: d3.ScaleLinear<number, number> | null = null
  yScale: d3.ScaleLinear<number, number> | null = null
  lineGenerator: d3.Line<Point> | null = null
  // 颜色比例尺
  colorScale: d3.ScaleOrdinal<number, string> | null = null
  colorDomain: number[] = []
  maxID: number = 14

  /**
   * 初始化类
   * @param trackSvg 类绑定的svg元素
   */
  constructor(trackSvg: SVGSVGElement) {
    this.svg = d3.select<SVGSVGElement, unknown>(trackSvg)
    this.svg
      .attr('width', this.size.width)
      .attr('height', this.size.height)
    // 设置颜色比例尺的 domain
    this.colorDomain = Array.from({ length: this.maxID + 1 }, (_, i) => i)
    this.colorScale = d3.scaleOrdinal<number, string>([
      '#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
      '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf',
      '#393b79', '#637939', '#8c6d31', '#843c39', '#7b4173'
    ]).domain(this.colorDomain)
  }

  /**
   * 初始化图表，设置比例尺，坐标轴
   * @param data 路径数据
   * @param video_data 视频数据，包括帧率，宽，高
   */
  public initChart(
    data: TrajectoryData[],
    video_data: { fps: number, width: number, height: number } | null) {
    this.svg.selectAll('*').remove()
    this.trackData = data

    // 设置比例尺
    let image_width = this.imageSize.width
    let image_height = this.imageSize.height
    let svg_width = this.size.width
    let svg_height = this.size.height
    if (video_data) {
      image_width = video_data.width
      image_height = video_data.height
      svg_width =
        (this.size.width - this.margin.left) / video_data.width * video_data.height + this.margin.left
      svg_height = this.size.height
    }
    this.xScale = d3.scaleLinear()
      .domain([0, image_width])
      .range([this.margin.left, svg_width - this.margin.left]);

    this.yScale = d3.scaleLinear()
      .domain([0, image_height])
      .range([this.margin.top, svg_height - this.margin.top]);

    // 创建坐标轴
    const xAxis = d3.axisTop(this.xScale).ticks(8);
    const yAxis = d3.axisLeft(this.yScale).ticks(8);

    // 添加坐标轴到SVG
    this.svg.selectAll('.x-axis').remove()
    this.svg.append('g')
      .attr('class', 'x-axis')
      .attr('transform', `translate(0, ${this.margin.top})`)
      .call(xAxis)
    this.svg.selectAll('.y-axis').remove()
    this.svg.append('g')
      .attr('class', 'y-axis')
      .attr('transform', `translate(${this.margin.left}, 0)`)
      .call(yAxis)
    // 创建线生成器
    this.lineGenerator = d3.line<Point>()
      .x(d => this.xScale!(d.x))
      .y(d => this.yScale!(d.y))
      .curve(d3.curveLinear); // 可以根据需要选择不同的曲线类型
  }

  /**
   * 根据绑定的数据渲染图表，绘制路径，圆点，id
   */
  public render() {
    const data = this.trackData
    if (!data || !this.xScale || !this.yScale || !this.colorScale || !this.lineGenerator) {
      return
    }
    const line = this.lineGenerator

    // 为每组新数据创建一个trackGroup
    let trackGroup = this.svg.selectAll('.track-group')
      .data(data)
      .enter()
      .append('g')
      .attr('class', 'track-group')
    trackGroup = this.svg.selectAll('.track-group')

    // 每个trackGroup创建一个path
    trackGroup.selectAll('path').remove() // 移除之前的path
    trackGroup.append('path')
      .attr('fill', 'none')
      .attr('stroke', (d) => this.colorScale!(d.id)) // 使用颜色比例尺
      .attr('stroke-width', 2)
      .attr('d', (d) => line(d.data));

    // 每个trackGroup添加圆点来表示每个数据点
    trackGroup.selectAll('circle')
      .data(d => {
        if (d.data.length > 1) {
          return [d.data[0], d.data[d.data.length - 1]]
        }
        else {
          return d.data
        }
      })
      .enter()
      .append('circle')
      .attr('class', 'track-point')
      .attr('cx', d => this.xScale!(d.x))
      .attr('cy', d => this.yScale!(d.y))
      .attr('r', 2) // 圆点半径
      .attr('fill', (d, i) => i == 0 ? 'black' : 'red'); // 圆点颜色

    // 每个trackGroup在最后一个数据点上显示id
    trackGroup.selectAll('text').remove() // 移除之前的text
    trackGroup.append('text')
      .datum((d) => {
        return { lastPoint: d.data[d.data.length - 1], id: d.id }
      })
      .attr('x', d => this.xScale!(d.lastPoint.x) + 5) // 在点的右侧显示
      .attr('y', d => this.yScale!(d.lastPoint.y))
      .attr('dy', '.35em') // 垂直居中
      .attr('fill', 'black')
      .text(d => `ID: ${d.id}`)
      .style('font-size', '15px');
  }
}

export type {
  TrajectoryData,
  Point
}
export {
  TrajectoryChart
}

